using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
namespace CSUtils
{
    /// <summary>
    /// 本地log保存
    /// </summary>
    public class CLogTxtSave
    {
        const int maxLogNumberLimit = 4000;
        #if UNITY_WINDOWS //win电脑
        const int fontSize = 1;
        const int fontSize2 = 1;
        #else
        const int fontSize = 12;
        const int fontSize2 = 1;
        #endif
        public static string saveRootDir { get; private set; }
        public static string currLogFilePath { get; private set; }
        public static string titleTag { get; private set; }
        public static bool isRealtimeSave { get; private set; } = true;

        public static string titleName;
        public static bool isThreadSplitSave;
        static string nowFileName;
        static int managedThreadId;

        static Dictionary<string, CLogTxtSave> threadName_logSaveObjDic = new Dictionary<string, CLogTxtSave>();

        string currThreadName;
        uint logIndex;
        uint fileIndex;
        uint currFileLogCount;
        StreamWriter streamWriter;
        List<MLog> logList = new List<MLog>();

        public static void Init(string rootDir, string midDir, bool isRealtimeSave = false, string titleTag = "log")
        {
            managedThreadId = Thread.CurrentThread.ManagedThreadId;
            //isThreadSplitSave = CSConfig.GetBool("CLogTxtSave_isThreadSplitSave", false, false);
            if (string.IsNullOrEmpty(rootDir))
            {
                rootDir = AppDomain.CurrentDomain.BaseDirectory + "/logList";
            }

            if (string.IsNullOrEmpty(midDir))
            {
                midDir = "log";
            } else
            {
                midDir = midDir.Replace("\\", "");
                //onlyName = onlyName.Replace("/", "");
                midDir = midDir.Replace(":", "");
                midDir = midDir.Replace("?", "");
                midDir = midDir.Replace("*", "");
                midDir = midDir.Replace("\"", "");
                midDir = midDir.Replace("<", "");
                midDir = midDir.Replace(">", "");
            }
            CLogTxtSave.titleTag = titleTag;
            saveRootDir = $"{rootDir}/{midDir}";
            saveRootDir = new DirectoryInfo(saveRootDir).FullName;
            saveRootDir = saveRootDir.Replace('\\', '/');
            nowFileName = $"{DateTime.Now.ToString("yyyy年MM月dd号/HH点mm分ss秒")}";
            titleName = DateTime.Now.ToString("HH.mm.ss");
            CLogTxtSave.isRealtimeSave = isRealtimeSave;
            LimiteLogfileNumber();
        }

        CLogTxtSave(string threadName)
        {
            currThreadName = threadName;
            fileIndex = 0;
            threadName_logSaveObjDic[threadName] = this;
        }

        static CLogTxtSave _instance;

        static string GetThreadName(int threadId)
        {
            var threadName = "";
            if (isThreadSplitSave)
            {
                threadName = Thread.CurrentThread.Name;
                if (threadId == managedThreadId)
                {
                    if (string.IsNullOrEmpty(threadName))
                    {
                        threadName = "unityMain";
                    }
                } else
                {
                    if (string.IsNullOrEmpty(threadName))
                    {
                        threadName = threadId + "";
                    }
                }
                threadName = $"/{threadName}";
            }
            return threadName;
        }

        static CLogTxtSave GetThreadObj()
        {
            if (isThreadSplitSave)
            {
                var threadId = Thread.CurrentThread.ManagedThreadId;
                var threadName = GetThreadName(threadId);
                if (threadName_logSaveObjDic.ContainsKey(threadName) == false)
                {
                    var aa = new CLogTxtSave(threadName);
                    aa.CreateNewLogFile();
                }
                return threadName_logSaveObjDic[threadName];
            } else
            {
                if (_instance == null)
                {
                    _instance = new CLogTxtSave("");
                    _instance.CreateNewLogFile();
                }
                return _instance;
            }
        }

        public void CreateNewLogFile()
        {
            currFileLogCount = 0;
            fileIndex++;
            var tag0 = "CSUtils".Length == 7 ? "" : "-local";
            currLogFilePath = $"{saveRootDir}/{nowFileName}{currThreadName}-{fileIndex}{tag0}.html";
            var ssb = new StringBuilder();
            ssb.Append($"<html lang='en'>\r\n<title>{titleTag}.{titleName}{currThreadName}-{fileIndex}{tag0}</title>\r\n");
            var _htmlFileHeaderStr = htmlFileHeaderStr.Replace("font-size: 1px", $"font-size: {fontSize}px");
            ssb.Append(_htmlFileHeaderStr);
            ssb.Append($"\t\t<font size = '3' color = '#00bb88' > {nowFileName}..{currThreadName}..{tag0}</font><br/>\r\n");
            ssb.Append("\t\t<font class='st'> 0> <br/></font>\r\n\r\n");
            CFile.WriteAllText(currLogFilePath, ssb.ToString(), Encoding.UTF8, null);
            if (streamWriter != null)
            {
                lock (streamWriter)
                {
                    streamWriter.Close();
                    streamWriter = new StreamWriter(currLogFilePath, true);
                }
            } else
            {
                streamWriter = new StreamWriter(currLogFilePath, true);
            }
        }

        public static string GetCurrNewFilePath(string _fileName)
        {
            var logObj = GetThreadObj();
            _fileName = Path.GetFileName(_fileName);
            var tag0 = "CSUtils".Length == 7 ? "" : "-local";
            var path = $"{saveRootDir}/{nowFileName}{logObj.currThreadName}-{logObj.fileIndex}{tag0}_{_fileName}";
            return path;
        }

        public static void AddLog(byte msgType, string tag, string currTime, string logStr, string stackTrace, string colorStr)
        {
            var logObj = GetThreadObj();
            logObj._AddLog(msgType, tag, currTime, logStr, stackTrace, colorStr);
        }

        void _AddLog(byte msgType, string tag, string currTime, string logStr, string stackTrace, string colorStr)
        {
            int index = logStr.IndexOf('\n');
            if (index > 0)
            {
                stackTrace = logStr.Substring(index) + "\n" + stackTrace;
                logStr = logStr.Substring(0, index);
            }
            if (string.IsNullOrEmpty(tag) == false)
            {
                logStr = tag + "..........." + logStr;
            }
            currFileLogCount++;
            if (isRealtimeSave)
            {
                _SaveLogData();
                if (currFileLogCount > maxLogNumberLimit)
                {
                    CreateNewLogFile();
                }
                var ssb = new StringBuilder();
                _ToLogStr(msgType, currTime, logStr, ++logIndex, stackTrace, colorStr, ssb);
                lock (streamWriter)
                {
                    streamWriter.Write(ssb.ToString()); //直接追加文件末尾，不换行
                }
            } else
            {
                MLog logObj;
                logObj.msgIndex = ++logIndex;
                logObj.msgType = msgType;
                logObj.time = currTime;
                logObj.logStr = logStr;
                logObj.stackTrace = stackTrace;
                logObj.colorStr = colorStr;
                logList.Add(logObj);
                if (logList.Count > 300)
                {
                    _SaveLogData();
                }
            }
        }

        public static void SaveAllLogData()
        {
            foreach (var kv in threadName_logSaveObjDic)
            {
                kv.Value._SaveLogData();
            }
        }

        public static void ClearAll(bool isRemove = true)
        {
            var arr = threadName_logSaveObjDic.Values.ToArray();
            for (int i = 0; i < arr.Length; i++)
            {
                var logSave = arr[i];
                logSave.Clear(isRemove);
            }
            _instance = null;
        }

        public void Clear(bool isRemove = true)
        {
            _SaveLogData();
            streamWriter.WriteLine("\n");
            streamWriter.Flush();
            streamWriter.Dispose();
            streamWriter.Close();
            if (isRemove)
            {
                threadName_logSaveObjDic.Remove(currThreadName);
            }
        }

        void _SaveLogData()
        {
            if (logList.Count == 0)
            {
                return;
            }

            if (currFileLogCount > maxLogNumberLimit)
            {
                CreateNewLogFile();
            }
            var ssb = new StringBuilder();
            for (int i = 0; i < logList.Count; i++)
            {
                var logObj = logList[i];
                _ToLogStr(logObj.msgType, logObj.time, logObj.logStr, logObj.msgIndex, logObj.stackTrace, logObj.colorStr, ssb);
            }
            //var file = new StreamWriter(currLogFilePath, true);
            lock (streamWriter)
            {
                streamWriter.Write(ssb.ToString()); //直接追加文件末尾，不换行
            }
            logList.Clear();
        }

        public static void _ToLogStr(byte msgType, string time, string logStr, uint msgIndex, string stackTrace, string color, StringBuilder ssb)
        {
            //logStr = logStr.GetLogColor(out string color);
            if (string.IsNullOrEmpty(color))
            {
                color = null;
            }

            switch (msgType)
            {
                case 0: //Error
                    if (color != null)
                    {
                        ssb.Append($"\t\t<font class='_err' size = '{fontSize2}' color = '#{color}' style='background-color:#220000' > {msgIndex}>--{logStr}<br/></font>\r\n");
                    } else
                    {
                        ssb.Append($"\t\t<font class='err'> {msgIndex}>--{logStr}<br/></font>\r\n");
                    }
                    break;
                case 1: //Assert
                    if (color != null)
                    {
                        ssb.Append($"\t\t<font class='_ass' size = '{fontSize2}' color = '#{color}' > {msgIndex}>--{logStr}<br/></font>\r\n");
                    } else
                    {
                        ssb.Append($"\t\t<font class='ass'> {msgIndex}>--{logStr}<br/></font>\r\n");
                    }
                    break;
                case 2: //Warning
                    if (color != null)
                    {
                        ssb.Append($"\t\t<font class='_war' size = '{fontSize2}' color = '#{color}' style='background-color:#444400' > {msgIndex}>--{logStr}<br/></font>\r\n");
                    } else
                    {
                        ssb.Append($"\t\t<font class='war'> {msgIndex}>--{logStr}<br/></font>\r\n");
                    }
                    break;
                case 3: //Log
                    if (color != null)
                    {
                        ssb.Append($"\t\t<font class='_log' size = '{fontSize2}' color = '#{color}' > {msgIndex}>--{logStr}<br/></font>\r\n");
                    } else
                    {
                        ssb.Append($"\t\t<font class='log'> {msgIndex}>--{logStr}<br/></font>\r\n");
                    }
                    break;
                case 4: //Exception
                    if (color != null)
                    {
                        ssb.Append($"\t\t<font class='_ex' size = '{fontSize2}' color = '#{color}' style='background-color:#990099' > {msgIndex}>--{logStr}<br/></font>\r\n");
                    } else
                    {
                        ssb.Append($"\t\t<font class='ex'> {msgIndex}>--{logStr}<br/></font>\r\n");
                    }
                    break;
                case 5:
                    if (color != null)
                    {
                        ssb.Append($"\t\t<font class='_ee' size = '{fontSize2}' color = '#{color}' style='background-color:#770088' > {msgIndex}>--{logStr}<br/></font>\r\n");
                    } else
                    {
                        ssb.Append($"\t\t<font class='ee'> {msgIndex}>--{logStr}<br/></font>\r\n");
                    }
                    break;
            }
            if (stackTrace == null)
            {
                stackTrace = "";
            }
            stackTrace = stackTrace.Replace('<', '(');
            ssb.Append($"\t\t<font class='st'> {time}===>{stackTrace.Replace("\n", "<br>")}</font>\r\n");
        }

        /// <summary>
        /// 限制logfile文件数量
        /// </summary> 
        public static void LimiteLogfileNumber(int limitNumber = 100)
        {
            if (Directory.Exists(saveRootDir) == false)
            {
                return;
            }
            DirectoryInfo directoryInfo = new DirectoryInfo(saveRootDir);
            FileInfo[] dirs = directoryInfo.GetFiles("*.*", SearchOption.AllDirectories);
            if (dirs.Length < limitNumber)
            {
                return;
            }

            dirs = dirs.OrderByDescending(n => n.CreationTime).ToArray();
            for (int i = limitNumber; i < dirs.Length; i++)
            {
                File.Delete(dirs[i].FullName);
            }
            DirectoryInfo[] dir0 = directoryInfo.GetDirectories("*.*", SearchOption.AllDirectories);
            for (int i = 0; i < dir0.Length; i++)
            {
                FileSystemInfo[] file0 = dir0[i].GetFileSystemInfos();
                if (file0.Length == 0)
                {
                    dir0[i].Delete();
                }
            }
        }

        struct MLog
        {
            public byte msgType;
            public uint msgIndex;
            public string logStr, stackTrace, time, colorStr;
        }

        public const string htmlFileHeaderStr = @"
    <head>
        <meta charset='UTF-8'>
        <style>
            body {
                margin: 0 auto;
                width: 10000px;
                height: 1000px;
                background-color: #002222;
            }
            
            .log {
                font-size: 1px;
                color: #609940;
            }
            
            .err {
                font-size: 1px;
                color: #880022;
                background-color: #221111;
            }
            
            .ass {
                font-size: 1px;
                color: #9999bb;
            }
            
            .war {
                font-size: 1px;
                color: #777700;
                background-color: #222211;
            }
            
            .ex {
                font-size: 1px;
                color: #ff77ff;
                background-color: #990077;
            }

            .ee {
                font-size: 1px;
                color: #777700;
                background-color: #771155;
            }

            font.st {
                font-size: 1px;
                color: #006060;
            }
            
            a.st {
                font-size: 1px;
                color: #999920;
            }
            
            .btn1 {
                z-index: 999; /*一个很大的层，保证不被其他元素覆盖*/
                position: fixed;
                top: 0; /*如果不是置顶，这里可以设置具体位置*/
            }
            
            .btn2 {
                z-index: 999; /*一个很大的层，保证不被其他元素覆盖*/
                position: fixed;
                top: 0; /*如果不是置顶，这里可以设置具体位置*/
                left: 110px;
            }
        </style>
        <script type='text/javascript'>
            window.onload = function () {
                function init_tag(_tabArr) {
                    for (let i = 0, len = _tabArr.length; i < len; i++) {
                        let tabObj = _tabArr[i];
                        if (tabObj.className === 'st') {
                            tabObj.style.display = 'none';
                        } else {
                            tabObj.style.display = 'block';
                            tabObj.onclick = function () {
                                let next = this.nextElementSibling;
                                //if (next.style.display == 'none') {
                                //    next.style.display = 'block';
                                //} else {
                                //    next.style.display = 'none';
                                //}
                                while (next != null && next.className === 'st') {
                                    if (next.style.display === 'none') {
                                        next.style.display = 'block';
                                    } else {
                                        next.style.display = 'none';
                                    }
                                    next = next.nextElementSibling;
                                }
                            }
                        }
                    }
                }
                
                let tabArr = []
                let fonts = document.getElementsByTagName('font');
                for (let i = 0; i < fonts.length; i++) {
                    tabArr[i] = fonts[i];
                }
                let as = document.getElementsByTagName('a');
                let len = tabArr.length;
                for (let i = 0; i < as.length; i++) {
                    tabArr[len + i] = as[i];
                }
                init_tag(tabArr);
                
                function isMatching(str, tagArr) {
                    for (let i4 = 0; i4 < tagArr.length; i4++) {
                        if (str === tagArr[i4]) {
                            return true;
                        }
                    }
                    return false;
                }
                
                function _onShowBtn(btnName, showName, tagArr) {
                    let exBtn = document.getElementById(btnName);
                    exBtn.isHideOther = false;
                    exBtn.onclick = function () {
                        window.scrollTo({top: 0, behavior: 'smooth'});//将页面滚动到顶部
                        exBtn.isHideOther = !exBtn.isHideOther;
                        exBtn.innerText = exBtn.isHideOther ? '显示全部log' : showName;
                        for (let i = 0, j = tabArr.length; i < j; i++) {
                            let tabObj = tabArr[i];
                            if (exBtn.isHideOther) {
                                if (isMatching(tabObj.className, tagArr)) {
                                    tabObj.style.display = 'block';
                                } else {
                                    tabObj.style.display = 'none';
                                }
                            } else {
                                if (tabObj.className === 'st') {
                                    tabObj.style.display = 'none';
                                } else {
                                    tabObj.style.display = 'block';
                                }
                            }
                        }
                    }
                }
                
                _onShowBtn('exBtn', '只显示异常log', ['ex', '_ex', 'ee', '_ee']);
                _onShowBtn('errBtn', '只显示错误log', ['ex', '_ex', 'ee', '_ee', 'err', '_err', 'err', '_err']);
            }
        </script>
    </head>
    <body>
        <button class='btn1' id='exBtn'>只显示异常log</button>
        <button class='btn2' id='errBtn'>只显示错误log</button>
        <br/><br/>
";
    }
}
